package com.yt.server.db.manage;

import java.io.File;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import org.dom4j.Document;
import org.dom4j.Element;

import com.yt.server.api.db.IDBManager;
import com.yt.server.db.Attribute;
import com.yt.server.db.DBSupport;
import com.yt.server.db.pool.DBPool;
import com.yt.server.db.pool.NetDBPool;
import com.yt.server.db.pool.SqliteDBPool;
import com.yt.server.db.thread.MonitorThread;
import com.yt.server.db.utils.ClusterList;
import com.yt.server.utils.FileUtils;
import com.yt.server.utils.XmlUtils;

/**
 * DB连接管理类
 * 
 * @version 2013.11.1 一版
 */
public class DBManager implements IDBManager {

	// 配置路径
	private String path;

	// 线程池容器
	private Map<String, DBPool> dbPool = null;

	// 集群容器
	private Map<String, ClusterList> cluster = null;

	// 缓存模块
	private int cacheTime = 1800;

	private boolean cache = false;

	private String cachePath = "cache";

	private MonitorThread monitorThread;

	private String jdbcCache = "jdbc/cache";

	private static DBManager instance = null;

	public static DBManager getInstance() {
		if (instance == null)
			instance = new DBManager();
		return instance;
	}

	/**
	 * 初始化
	 * 
	 * @param path
	 */
	public void init(String path) {
		if (dbPool == null)
			dbPool = new HashMap<String, DBPool>();
		if (cluster == null)
			cluster = new HashMap<String, ClusterList>();
		try {
			this.path = path;
			Document document = XmlUtils.getFileDocument(path);
			Map<String, String> root = XmlUtils.getAllAttribute(document
					.getRootElement());
			cache = Boolean.parseBoolean(root.get(Attribute.CACHE));
			cacheTime = Integer.parseInt(root.get(Attribute.CACHE_TIME));
			cachePath = root.get(Attribute.CACHE_PATH);
			root = null;

			Map<String, String> attributes = null;
			// 初始化数据库
			List<Element> elements = XmlUtils.getAllElement(
					document.getRootElement(), Attribute.RESOURCE);
			for (Element element : elements) {
				attributes = XmlUtils.getAllAttribute(element);
				// sqlite处理速度高于Mysql、PostgreSQL
				if (String.valueOf(attributes.get(Attribute.DRIVER_CLASS))
						.equals("org.sqlite.JDBC")) {
				}
				// HBase分布式的、面向列的开源数据库
				else if (String.valueOf(attributes.get(Attribute.DRIVER_CLASS))
						.equals("org.apache.hadoop.hive.jdbc.HiveDriver")) {
				}
				// 其他
				else {
					DBPool pool = new NetDBPool();
					pool.init(String.valueOf(attributes.get("userName")),
							String.valueOf(attributes.get("passWord")), String
									.valueOf(attributes.get("url")), String
									.valueOf(attributes
											.get(Attribute.DRIVER_CLASS)),
							Integer.parseInt(String.valueOf(attributes
									.get("initSize"))), Integer.parseInt(String
									.valueOf(attributes.get("minPoolSize"))),
							Integer.parseInt(String.valueOf(attributes
									.get("maxPoolSize"))), Integer
									.parseInt(String.valueOf(attributes
											.get("maxStatements"))), Integer
									.parseInt(String.valueOf(attributes
											.get("maxIdleTime"))));
					dbPool.put(String.valueOf(attributes.get(Attribute.JNDI)),
							pool);
				}
			}
			elements = null;

			// 加载集群
			List<Element> clusterList = XmlUtils.getAllElement(
					document.getRootElement(), Attribute.CLUSTER);
			for (Element element : clusterList) {
				List<Element> resourceList = XmlUtils.getAllElement(element,
						Attribute.RESOURCE);
				ClusterList resourceNameList = new ClusterList();
				for (Element e : resourceList) {
					Map att = XmlUtils.getAllAttribute(e);
					resourceNameList
							.add(String.valueOf(att.get(Attribute.JNDI)));
					att = null;
				}
				resourceList = null;
				attributes = XmlUtils.getAllAttribute(element);
				// 设置主要数据库
				resourceNameList.setMainDB(attributes.get(Attribute.MAIN_DB));
				cluster.put(attributes.get(Attribute.JNDI_CLUSTER),
						resourceNameList);
				attributes = null;
			}
			clusterList = null;
			startCache();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 启动缓存模块
	 */
	public void startCache() {
		if (monitorThread != null)
			return;
		if (cache) {
			initCacheNewJndi(cachePath);
			DBSupport
					.getInstance()
					.update(jdbcCache,
							"create table cache(sql varchar(20000),jndi varchar(50), result varchar(60000),time varchar(20));");
			monitorThread = new MonitorThread();
			monitorThread.setMonitorState(true);
			monitorThread.setCacheTime(this.cacheTime);
			monitorThread.start();
			System.out.println("缓存模块启动完成!!!!");
		}
	}

	/**
	 * 启动缓存模块
	 * 
	 * @param cache
	 * @param cacheTime
	 * @param cachePath
	 */
	public void startCache(boolean cache, int cacheTime, String cachePath) {
		this.cache = cache;
		this.cacheTime = cacheTime;
		this.cachePath = cachePath;
		startCache();
	}

	/**
	 * 初始化新的缓存库
	 * 
	 * @param dbPath
	 */
	public void initCacheNewJndi(String dbPath) {
		if (!FileUtils.checkFolder(System.getProperty("user.dir")
				+ File.separator + dbPath)) {
			FileUtils.createFolder(System.getProperty("user.dir")
					+ File.separator + dbPath);
		}
		if (FileUtils.checkFile(System.getProperty("user.dir") + File.separator
				+ dbPath + File.separator + "cache.db")) {
			FileUtils.deleteFile(System.getProperty("user.dir")
					+ File.separator + dbPath + File.separator + "cache.db");
		}
		StringBuffer jdbcUrl = new StringBuffer();
		jdbcUrl.append("jdbc:sqlite:");
		jdbcUrl.append(System.getProperty("user.dir") + File.separator);
		jdbcUrl.append(dbPath);
		jdbcUrl.append(File.separator);
		jdbcUrl.append("cache.db");
		System.out.println(jdbcUrl.toString());
		initSqliteJndi(jdbcCache, jdbcUrl.toString());
		jdbcUrl = null;
	}

	/**
	 * 初始化新的jndi
	 * 
	 * @param jndi
	 * @param dbUserName
	 * @param dbPassWord
	 * @param jdbcUrl
	 * @param driverClass
	 */
	public void initNewJndi(String jndi, String dbUserName, String dbPassWord,
			String jdbcUrl, String driverClass) {
		initNewJndi(jndi, dbUserName, dbPassWord, jdbcUrl, driverClass, 2, 1,
				10, 50, 60);
	}

	/**
	 * 初始化新的jndi
	 * 
	 * @param jndi
	 * @param dbUserName
	 * @param dbPassWord
	 * @param jdbcUrl
	 * @param driverClass
	 * @param initSize
	 * @param minPoolSize
	 * @param maxPoolSize
	 * @param maxStatements
	 * @param maxIdleTime
	 */
	public void initNewJndi(String jndi, String dbUserName, String dbPassWord,
			String jdbcUrl, String driverClass, int initSize, int minPoolSize,
			int maxPoolSize, int maxStatements, int maxIdleTime) {
		if (dbPool == null)
			dbPool = new HashMap<String, DBPool>();
		DBPool pool = new NetDBPool();
		pool.init(dbUserName, dbPassWord, jdbcUrl, driverClass, initSize,
				minPoolSize, maxPoolSize, maxStatements, maxIdleTime);
		dbPool.put(jndi, pool);
	}

	/**
	 * 初始sqlite数据库
	 * 
	 * @param jndi
	 * @param jdbcUrl
	 */
	public void initSqliteJndi(String jndi, String jdbcUrl) {
		if (dbPool == null)
			dbPool = new HashMap<String, DBPool>();
		String driverClass = "org.sqlite.JDBC";
		DBPool pool = new SqliteDBPool();
		pool.init("", "", jdbcUrl.toString(), driverClass);
		dbPool.put(jndi, pool);
	}

	/**
	 * 初始化mysql jndi连接池
	 * 
	 * @param jndi
	 * @param ip
	 * @param port
	 * @param dbUserName
	 * @param dbPassWord
	 */
	public void initMysqlJndi(String jndi, String ip, String port,
			String dbName, String dbUserName, String dbPassWord) {
		String driverClass = "com.mysql.jdbc.Driver";
		StringBuffer jdbcUrl = new StringBuffer();
		jdbcUrl.append("jdbc:mysql://");
		jdbcUrl.append(ip);
		jdbcUrl.append(":");
		jdbcUrl.append(port);
		jdbcUrl.append("/");
		jdbcUrl.append(dbName);
		jdbcUrl.append("?useUnicode=true&amp;characterEncoding=utf-8");
		// 初始化连接池
		initNewJndi(jndi, dbUserName, dbPassWord, jdbcUrl.toString(),
				driverClass);
		jdbcUrl = null;
		driverClass = null;
	}

	/**
	 * 获取默认数据库连接
	 * 
	 * @return
	 */
	public final Connection getConnection() {
		try {
			return getDefaultDBPool().getConnection();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 获取指定数据库的连接
	 * 
	 * @param jndi
	 * @return
	 */
	public final Connection getConnection(String jndi) {
		try {
			return dbPool.get(jndi).getConnection();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 获取集群的连接器
	 * 
	 * @param clusterJndi
	 * @return
	 */
	public final Connection getClusterConnection(String clusterJndi) {
		try {
			ClusterList clusterList = cluster.get(clusterJndi);
			// 检查主连接数据库是否正常
			if (this.testCheckoutConnection(clusterList.getMainDB()))
				return null;

			String idleJndiName = null;
			int idleJndiNum = 999;
			for (String clusterJndiName : clusterList) {
				if (this.getNumBusyConnection(clusterJndiName) == 0) {
					idleJndiName = clusterJndiName;
					continue;
				}
				if (this.testCheckoutConnection(clusterJndiName)
						&& idleJndiNum > this
								.getNumBusyConnection(clusterJndiName)) {
					idleJndiNum = this.getNumBusyConnection(clusterJndiName);
					idleJndiName = clusterJndiName;
				}
			}
			clusterList = null;
			// 输出集群查询的数据库jndi
			System.out.println("集群查询的数据库" + idleJndiName);
			return dbPool.get(idleJndiName).getConnection();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 获取集群数据库连接器列表
	 * 
	 * @param clusterJndi
	 * @return
	 */
	public final List<Connection> getClusterConnectionList(String clusterJndi) {
		List<Connection> list = null;
		try {
			list = new ArrayList<Connection>();
			ClusterList clusterList = cluster.get(clusterJndi);
			for (String clusterJndiName : clusterList)
				list.add(dbPool.get(clusterJndiName).getConnection());
		} catch (Exception e) {
			e.printStackTrace();
		}
		return list;
	}

	/**
	 * 获取集群数据库jndi列表
	 * 
	 * @param clusterJndi
	 * @return
	 */
	public final ClusterList getClusterConnectionNameList(String clusterJndi) {
		ClusterList list = null;
		try {
			list = cluster.get(clusterJndi);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return list;
	}

	/**
	 * 获取连接池空闲连接数
	 * 
	 * @param jndi
	 * @return
	 * @throws SQLException
	 */
	public int getNumIdleConection(String jndi) throws SQLException {
		return dbPool.get(jndi).getNumIdleConection();
	}

	/**
	 * 获取连接池繁忙连接数
	 * 
	 * @param jndi
	 * @return
	 * @throws SQLException
	 */
	public int getNumBusyConnection(String jndi) throws SQLException {
		return dbPool.get(jndi).getNumBusyConnection();
	}

	/**
	 * 检查该连接池是否正常
	 * 
	 * @param jndi
	 * @return
	 * @throws SQLException
	 */
	public boolean testCheckoutConnection(String jndi) throws SQLException {
		return dbPool.get(jndi).testCheckoutConnection();
	}

	/**
	 * 获取默认连接
	 * 
	 * @return
	 */
	private DBPool getDefaultDBPool() {
		Iterator<String> it = dbPool.keySet().iterator();
		while (it.hasNext()) {
			return dbPool.get(it.next().toString());
		}
		return null;
	}

	/**
	 * 判断指定JNDI的数据库连接池是否存在
	 * 
	 * @param jndi
	 * @return
	 */
	public boolean isDBPoolExists(String jndi) {
		return dbPool.get(jndi) == null ? false : true;
	}

	public String getPath() {
		return path;
	}

	public Map<String, DBPool> getDbPool() {
		return dbPool;
	}

	public Map<String, ClusterList> getCluster() {
		return cluster;
	}

	public int getCacheTime() {
		return cacheTime;
	}

	public boolean isCache() {
		return cache;
	}

	public String getCachePath() {
		return cachePath;
	}

	public MonitorThread getMonitorThread() {
		return monitorThread;
	}

	public String getJdbcCache() {
		return jdbcCache;
	}

	public void setPath(String path) {
		this.path = path;
	}

	public void setCache(boolean cache) {
		this.cache = cache;
	}

	public void setCacheTime(int cacheTime) {
		this.cacheTime = cacheTime;
	}

	public void setCachePath(String cachePath) {
		this.cachePath = cachePath;
	}

	public void setDbPool(Map<String, DBPool> dbPool) {
		this.dbPool = dbPool;
	}

	public static void main(String[] args) {
		DBManager.getInstance().init(
				System.getProperty("user.dir")
						+ "\\src\\com\\yt\\server\\db\\config\\db.xml");
	}
}